#include <stdio.h>
#include <conio.h>
#include <peekpoke.h>
#include <string.h>

#include "Shapes.h"
#include "Types.h"
#include "Output.h"

byte_t sprites1[0x0700];
byte_t sprites2[0x0700];


byte_t* getShape(shapes_e shape)
{
	static byte_t* result;

	result = NULL;

	switch(shape)
	{
	case S_LINE: result = _line; break;
	case S_BOX: result = _box; break;
	case S_TEE: result = _tee; break;
	case S_LEFT_L: result = _left_l; break;
	case S_RIGHT_L: result = _right_l; break;
	case S_LEFT_S: result = _left_s; break;
	case S_RIGHT_S: result = _right_s; break;
	}
	
	return result;
}

void makeSprites(void)
{
	byte_t shape;
	byte_t orient;
	byte_t *current;

	for(shape = S_LINE; shape <= S_RIGHT_S; ++shape)
	{
		for(orient = O_UP; orient <= O_RIGHT; orient += 16)
		{
			current = getShape((shapes_e)shape);
			buildSprite(current + orient);
			memcpy(sprites1 + shape * 64 * 4 + (orient / 16) * 64, (void*)SPRITE1, 64);
			memcpy(sprites2 + shape * 64 * 4 + (orient / 16) * 64, (void*)SPRITE2, 64);
		}
	}
}

void getSprite(shapes_e shape, orientations_e orient)
{
	memcpy((void*)SPRITE1, &sprites1[shape * 64 * 4 + (orient / 16) * 64], 64);
	memcpy((void*)SPRITE2, &sprites2[shape * 64 * 4 + (orient / 16) * 64], 64);
}



void __fastcall__ buildSprite(byte_t *shapeDefinition)
{
	static byte_t j, k;

	setSprite(shapeDefinition);

	if(shapeDefinition == _line + 16 || shapeDefinition == _line + 48)
	{
		for(k = 0; k < 8; ++k)
		{
			j = shapeDefinition[3];
			POKE(SPRITE2 + k * 3, 
				PEEK(0x3000 + 8 * j + k));
		}
	}
}

bool drawShape(byte_t x, byte_t y, shapes_e shape, byte_t color, orientations_e orient, bool testRight, bool testLeft, bool testDown, bool finalize)
{
	static byte_t i, j, c;
	static bool collision, current;
	static byte_t *shapeDefinition;
	static bool result[4];
	static bool initialized = false;
	static shapes_e last_shape = S_LINE;
	static orientations_e last_orient = O_UP;

	collision = false;
	current = false;

	shapeDefinition = &getShape(shape)[(unsigned)orient];

	_fore_color = color;
	
	if(testLeft)
	{
		for(i = 0; i < 4; ++i)
		{
			c = *((char*)(CHARMAP + (x - 1) + (y + i) * 40));
			if(c != 32)
			{
				if(shapeDefinition[i * 4] != 32) return false;
			}
		}

		return true;
	}
	else if(testRight)
	{
		for(i = 0; i < 4; ++i)
		{
			result[i] = true;
			for(j = 4; j > 0; --j)
			{
				if(shapeDefinition[j - 1 + i * 4] != 32)
				{
					if(*((char*)(CHARMAP + (x + j) + (y + i) * 40)) != 32)	
					{
						result[i] = false;
					}

					break;
				}
			}
		}

		return (result[0] & result[1] & result[2] & result[3]);
	}
	else
	{
		j = y;
		for(i = 0; i < 12; ++i)
		{
			 if(*(shapeDefinition + i) != 32) 
			{ 
				current = codeat(x + (i % 4), j + 1) != 32 && *(shapeDefinition + i + 4) == 32; 
				collision |= current;
			}
			if(i % 4 == 3) ++j;
		}

		for(; i < 16; ++i)
		{
			if(*(shapeDefinition + i) != 32) 
			{ 
				current = codeat(x + (i % 4), j + 1) != 32; 
				collision |= current;
			}
		}

		if(finalize)
		{
			j = y;

			for(i = 0; i < 16; ++i)
			{
				if(*(shapeDefinition + i) != 32) 
				{ 
					plotcodexy(x + (i % 4), j, *(shapeDefinition + i)); 
				}
				if(i % 4 == 3) ++j;
			}

			POKE(0xD015, 0);
		}
		else if(!testDown)
		{
			if(!initialized)
			{
				initialized = true;
				getSprite(shape, orient);
			}
			else
			{
				if(last_shape != shape || last_orient != orient)
				{
					getSprite(shape, orient);
				}
			}

			last_shape = shape;
			last_orient = orient;

			POKE(0x07F8, SPRITE1 / 64);
			POKE(0xD027, color);

			POKE(0x07F9, SPRITE2 / 64);
			POKE(0xD028, color);

			POKE(0xD000, x * 8 + 24);
			POKE(0xD001, y * 8 + 50);

			if(shape == S_LINE && (orient == O_LEFT || orient == O_RIGHT))
			{
				POKE(0xD002, (x + 3) * 8 + 24);
				POKE(0xD003, y * 8 + 50); 
			}
			else
			{
				POKE(0xD002, x * 8 + 24);
				POKE(0xD003, (y + 2) * 8 + 50); 
			}

			POKE(0xD015, 3);
		}
	}

	return collision;
}
